package no.ntnu.fp.gui.calendar;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeListener;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.swing.BoxLayout;
import javax.swing.JPanel;
import javax.swing.JScrollPane;

import no.ntnu.fp.gui.mainFrame.ModelListenerProvider;
import no.ntnu.fp.model.Appointment;
import no.ntnu.fp.model.Employee;
import no.ntnu.fp.model.Meeting;
import no.ntnu.fp.model.events.AppointmentsChangedEvent;
import no.ntnu.fp.model.events.AppointmentsClearedEvent;
import no.ntnu.fp.model.events.MeetingsChangedEvent;
import no.ntnu.fp.model.events.MeetingsClearedEvent;
import no.ntnu.fp.model.events.ModelEvent;
import no.ntnu.fp.model.listeners.ModelListener;

@SuppressWarnings("serial")
public class CalendarPanel extends JPanel implements ModelListenerProvider{
	private final DayPanel[] days = {new DayPanel(), new DayPanel(), new DayPanel(),
			new DayPanel(), new DayPanel(), new DayPanel(), new DayPanel()};

	//	private int week, year;
	private final DatePanel dates;
	private HashMap<Employee, List<Appointment>> appointments;
	private HashMap<Employee, List<Meeting>> meetings;
	private SelectWeekPanel selectWeek;

	private final Calendar calendar;

	private static CalendarPanel instance;

	public static void createInstance(Calendar calendar) {
		if (instance == null) {
			instance = new CalendarPanel(calendar);
		}else{
			throw new IllegalStateException("CalendarPanel instance exists!");
		}
	}

	public static CalendarPanel getInstance() {
		if(instance == null){
			throw new IllegalStateException("CalendarPanel instance does not exist!");
		}else{
			return instance;
		}
	}

	public void addPropertyChangeListener(PropertyChangeListener listener){
		for(DayPanel day : days){
			day.addPropertyChangeListener(listener);
		}
	}

	private CalendarPanel(Calendar calendar){
		this.calendar = calendar;
		JPanel pane = new JPanel();
		dates = new DatePanel(calendar);
		selectWeek = new SelectWeekPanel(calendar);
		pane.add(new TimePanel());
		for(int i = 0; i < 7; i++){
			pane.add(days[i]);
		}
		//		week = Calendar.getInstance().get(Calendar.WEEK_OF_YEAR);
		//		year = Calendar.getInstance().get(Calendar.YEAR);
		appointments = new HashMap<Employee, List<Appointment>>();
		meetings = new HashMap<Employee, List<Meeting>>();

		//		dates.setWeek(week, year);
		//		selectWeek.setWeek(week);
		JScrollPane scroll = new JScrollPane(pane);
		scroll.getVerticalScrollBar().setUnitIncrement(8);
		this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
		this.add(selectWeek);
		this.add(dates);
		this.add(scroll);
		days[0].scrollRectToVisible(new Rectangle(0, 425, 1, 1));
	}
	public void setSelectWeekListeners(ActionListener next, ActionListener prev){
		selectWeek.setNextWeekActionListener(next);
		selectWeek.setPrevoiusWeekActionListener(prev);
	}

	public void setEmployee(Employee emp){
		for(DayPanel day: days){
			day.setCurrentEmployee(emp);
		}
	}

	private final class TimePanel extends JPanel{
		public TimePanel(){
			this.setPreferredSize(new Dimension(40, 1500));
		}
		@Override
		protected void paintComponent(Graphics g) {
			super.paintComponent(g);
			Graphics2D g2d = (Graphics2D) g;
			g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, 
					RenderingHints.VALUE_TEXT_ANTIALIAS_ON);

			double hourHeight = this.getHeight() / 24;

			g2d.setColor(Color.BLUE);
			for(int i = 1; i < 24; i++){
				int y = (int)hourHeight*i;
				String title = (i < 10) ? "0" + i : i + "";
				title += ":00";
				g2d.drawString(title, 0, y);
			}

		}
	}

	public void repaintAll(){
		for(DayPanel day : days){
			day.repaint();
		}
	}

	//	public int getWeek(){
	//		return this.week;
	//	}
	//	
	//	public int getYear() {
	//		return this.year;
	//	}

	public void updateDate() {
		//		Calendar cal = new GregorianCalendar();
		////		cal.clear();
		//		cal.setFirstDayOfWeek(Calendar.MONDAY);
		//		
		//		if (week < 1) {
		//			cal.clear();
		//			cal.set(Calendar.YEAR, year);
		//			cal.set(Calendar.DATE, 0);
		//		} else {
		//			cal.set(Calendar.YEAR, year);
		//			cal.set(Calendar.WEEK_OF_YEAR, week);
		//		}
		//	
		//		this.year = cal.get(Calendar.YEAR);
		//		this.week = cal.get(Calendar.WEEK_OF_YEAR);
		//		
		/*
		if(week > 52){
			this.week = 1;
			this.year ++;
		}else if(week < 1){
			this.week = 52;
			this.year --;
		}else{
			this.week = week;
		}
		 */

		//		dates.setWeek(this.week, this.year);
		//		selectWeek.setWeek(this.week);

		dates.updateDate();
		selectWeek.updateDate();

		Calendar cal = (Calendar)calendar.clone();

		cal.set(Calendar.MINUTE, 0);
		cal.set(Calendar.SECOND, 0);
		cal.set(Calendar.MILLISECOND, -1);
		cal.set(Calendar.HOUR_OF_DAY, 0);
		cal.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
		Date firstDay = cal.getTime();
		cal.set(Calendar.WEEK_OF_YEAR, calendar.get(Calendar.WEEK_OF_YEAR));
		cal.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY);
		cal.set(Calendar.MINUTE, 0);
		cal.set(Calendar.SECOND, 0);
		cal.set(Calendar.MILLISECOND, 0);
		cal.set(Calendar.HOUR_OF_DAY, 24);
		Date endDay = cal.getTime();

		for(DayPanel day : days){
			day.clear();
			day.repaint();
		}
		for(Employee e: meetings.keySet()){
			for(Meeting m : meetings.get(e)){
				if(!(m.getDate().before(firstDay) || m.getDate().after(endDay))){
					cal.setTime(m.getDate());
					if(cal.get(Calendar.DAY_OF_WEEK) != Calendar.SUNDAY){
						days[cal.get(Calendar.DAY_OF_WEEK) - 2].addMeeting(m);
					}else{
						days[6].addMeeting(m);
					}
				}
			}
		}
		for(Employee e: appointments.keySet()){
			for(Appointment a : appointments.get(e)){
				if(!(a.getDate().before(firstDay) || a.getDate().after(endDay))){
					cal.setTime(a.getDate());
					if(cal.get(Calendar.DAY_OF_WEEK) != Calendar.SUNDAY){
						days[cal.get(Calendar.DAY_OF_WEEK) - 2].addAppointment(a);
					}else{
						days[6].addAppointment(a);
					}
				}
			}
		}
	}
	private final class AppointmentsChangeListener implements ModelListener<AppointmentsChangedEvent>{
		@Override
		public void handleEvent(AppointmentsChangedEvent event) {
			appointments.put(event.getEmp(), event.getNewAppointments());
			updateDate();
			//			CalendarPanel.this.setWeekAndYear(
			//					CalendarPanel.this.getWeek(), CalendarPanel.this.getYear());
		}
	}
	private final class MeetingsChangeListener implements ModelListener<MeetingsChangedEvent>{
		@Override
		public void handleEvent(MeetingsChangedEvent event) {
			meetings.put(event.getEmp(), event.getNewMeetings());
			updateDate();

			//			CalendarPanel.this.setWeekAndYear(
			//					CalendarPanel.this.getWeek(), CalendarPanel.this.getYear());
		}
	}

	private final class AppointmentsClearedListener implements ModelListener<AppointmentsClearedEvent> {
		@Override
		public void handleEvent(AppointmentsClearedEvent event) {
			appointments.clear();
			updateDate();
		}
	}

	private final class MeetingsClearedListener implements ModelListener<MeetingsClearedEvent> {
		@Override
		public void handleEvent(MeetingsClearedEvent event) {
			meetings.clear();
			updateDate();
		}
	}

	@Override
	public Map<Class<? extends ModelEvent>, ModelListener<?>> getEventListeners() {
		Map<Class<? extends ModelEvent>, ModelListener<?>> map = 
			new HashMap<Class<? extends ModelEvent>, ModelListener<?>>();
		map.put(AppointmentsChangedEvent.class, new AppointmentsChangeListener());
		map.put(MeetingsChangedEvent.class, new MeetingsChangeListener());
		map.put(AppointmentsClearedEvent.class, new AppointmentsClearedListener());		
		map.put(MeetingsClearedEvent.class, new MeetingsClearedListener());
		return map;
	}
}
